"""
内容检测器模块，集成书本检测、手指检测和图像处理功能，实现完整的指向内容检测。
"""
import cv2
import numpy as np
from typing import List, Tuple, Dict, Optional, Union, Any
from dataclasses import dataclass
import time

from kidsbuddy.tools.camera import CameraManager
from book_detector import BookDetector, DetectionResult
from finger_detector import FingerDetector, FingerDetection
from kidsbuddy.tools.image_processor import ImageProcessor, ContentRegion


@dataclass
class PointedContentResult:
    """指向内容检测结果数据类"""
    # 书本检测结果
    book_detection: Optional[DetectionResult]
    # 书本在原始图像中的边界框 (x, y, w, h)
    book_box: Optional[Tuple[int, int, int, int]]
    # 指向内容的边界框 (相对于书本区域的坐标) (x, y, w, h)
    content_box_relative: Optional[Tuple[int, int, int, int]]
    # 指向内容的边界框 (相对于原始图像的坐标) (x, y, w, h)
    content_box_absolute: Optional[Tuple[int, int, int, int]]
    # 原始图像中书本的裁剪图像
    book_image: Optional[np.ndarray]
    # 书本图像的二值化结果
    book_binary: Optional[np.ndarray]
    # 指向内容的裁剪图像
    content_image: Optional[np.ndarray]
    # 检测到的手指信息
    finger_detection: Optional[FingerDetection]
    # 处理时间 (毫秒)
    processing_time: float
    # 是否成功检测到内容
    success: bool
    # 错误信息 (如果有)
    error_message: Optional[str]


class ContentDetector:
    """
    内容检测器类，使用外观模式(Facade Pattern)集成所有组件，
    提供简单的接口用于检测书本和指向的内容。
    """

    def __init__(
        self,
        camera_id: int = 0,
        book_model_path: Optional[str] = None,
        book_confidence_threshold: float = 0.5,
        finger_max_hands: int = 2,
        finger_min_detection_confidence: float = 0.7,
        binarization_method: str = 'adaptive',
        debug_mode: bool = False
    ):
        """
        初始化内容检测器

        参数:
            camera_id: 摄像头ID
            book_model_path: 书本检测模型路径
            book_confidence_threshold: 书本检测置信度阈值
            finger_max_hands: 最大手部检测数量
            finger_min_detection_confidence: 手部检测最小置信度
            binarization_method: 二值化方法
            debug_mode: 是否启用调试模式
        """
        self.debug_mode = debug_mode

        # 初始化摄像头管理器
        self.camera = CameraManager(camera_id=camera_id)

        # 初始化书本检测器
        self.book_detector = BookDetector(
            model_path=book_model_path,
            confidence_threshold=book_confidence_threshold
        )

        # 初始化手指检测器
        self.finger_detector = FingerDetector(
            max_num_hands=finger_max_hands,
            min_detection_confidence=finger_min_detection_confidence
        )

        # 初始化图像处理器
        self.image_processor = ImageProcessor(
            binarization_method=binarization_method
        )

        # 预热组件
        self._warmup()

        print("内容检测器初始化完成")

    def _warmup(self):
        """预热各组件，加载模型和初始化资源"""
        try:
            # 预热摄像头
            self.camera.open()
            self.camera.capture_frame()

            # 其他组件在使用时会自动初始化
            print("组件预热完成")
        except Exception as e:
            print(f"组件预热失败: {str(e)}")

    def detect_content(self, image: Optional[np.ndarray] = None) -> PointedContentResult:
        """
        检测图像中被手指指向的内容

        参数:
            image: 输入图像，如果为None则自动从摄像头获取

        返回:
            PointedContentResult: 检测结果
        """
        start_time = time.time()

        # 如果未提供图像，则从摄像头获取
        if image is None:
            image = self.camera.capture_frame()
            if image is None:
                return PointedContentResult(
                    book_detection=None,
                    book_box=None,
                    content_box_relative=None,
                    content_box_absolute=None,
                    book_image=None,
                    book_binary=None,
                    content_image=None,
                    finger_detection=None,
                    processing_time=0,
                    success=False,
                    error_message="无法获取摄像头图像"
                )

        # 检测书本
        book_detections = self.book_detector.detect_books(image)

        if not book_detections:
            return PointedContentResult(
                book_detection=None,
                book_box=None,
                content_box_relative=None,
                content_box_absolute=None,
                book_image=None,
                book_binary=None,
                content_image=None,
                finger_detection=None,
                processing_time=(time.time() - start_time) * 1000,
                success=False,
                error_message="未检测到书本"
            )

        # 选择置信度最高的书本
        best_book = max(book_detections, key=lambda d: d.confidence)
        book_box = best_book.box

        # 提取书本区域
        x, y, w, h = book_box
        book_image = image[y:y+h, x:x+w].copy()

        # 对书本图像进行二值化处理
        book_binary = self.image_processor.binarize_image(book_image)

        # 提取内容区域
        content_regions = self.image_processor.extract_content_regions(
            book_binary, min_area=50)

        # 检测手指
        finger_detections = self.finger_detector.detect_fingers(image)

        if not finger_detections:
            return PointedContentResult(
                book_detection=best_book,
                book_box=book_box,
                content_box_relative=None,
                content_box_absolute=None,
                book_image=book_image,
                book_binary=book_binary,
                content_image=None,
                finger_detection=None,
                processing_time=(time.time() - start_time) * 1000,
                success=False,
                error_message="未检测到手指"
            )

        # 获取指向方向
        pointing_info = self.finger_detector.get_pointing_direction(
            finger_detections,
            prefer_finger_types=[1, 2]  # 优先选择食指和中指
        )

        if pointing_info is None:
            return PointedContentResult(
                book_detection=best_book,
                book_box=book_box,
                content_box_relative=None,
                content_box_absolute=None,
                book_image=book_image,
                book_binary=book_binary,
                content_image=None,
                finger_detection=finger_detections[0] if finger_detections else None,
                processing_time=(time.time() - start_time) * 1000,
                success=False,
                error_message="无法确定指向方向"
            )

        finger_tip, finger_direction = pointing_info
        best_finger = next(
            (f for f in finger_detections if f.tip_position == finger_tip), None)

        # 创建手指方向掩码 (相对于原始图像)
        finger_mask_full = self.image_processor.create_finger_direction_mask(
            (image.shape[0], image.shape[1]),
            finger_tip,
            finger_direction,
            length_factor=1.5  # 增加长度，确保覆盖指向区域
        )

        # 检查手指掩码是否与书本区域重叠
        overlap_with_book = False
        book_mask = np.zeros_like(image, dtype=np.uint8)
        book_mask[y:y+h, x:x+w] = 255
        overlap = cv2.bitwise_and(finger_mask_full[:, :, 0] if len(finger_mask_full.shape) > 2 else finger_mask_full,
                                  book_mask[:, :, 0] if len(book_mask.shape) > 2 else book_mask)
        if np.count_nonzero(overlap) > 0:
            overlap_with_book = True

        # 如果手指掩码与书本没有重叠，可能指向的不是书本
        if not overlap_with_book:
            if self.debug_mode:
                print("手指未指向书本")

            # 尝试扩大掩码
            finger_mask_full = self.image_processor.create_finger_direction_mask(
                (image.shape[0], image.shape[1]),
                finger_tip,
                finger_direction,
                length_factor=2.5  # 更大的长度
            )

            # 再次检查
            overlap = cv2.bitwise_and(finger_mask_full[:, :, 0] if len(finger_mask_full.shape) > 2 else finger_mask_full,
                                      book_mask[:, :, 0] if len(book_mask.shape) > 2 else book_mask)
            overlap_with_book = np.count_nonzero(overlap) > 0

            if not overlap_with_book and self.debug_mode:
                print("即使扩大掩码，手指仍未指向书本")

        # 创建手指方向掩码 (相对于书本图像)
        # 将指尖坐标转换到书本坐标系
        book_finger_tip = (
            max(0, finger_tip[0] - x),
            max(0, finger_tip[1] - y)
        )

        book_finger_mask = self.image_processor.create_finger_direction_mask(
            (book_image.shape[0], book_image.shape[1]),
            book_finger_tip,
            finger_direction,
            length_factor=1.5  # 增加长度
        )

        # 查找指向的内容区域
        pointed_region = self.image_processor.find_pointed_content(
            content_regions,
            book_finger_mask,
            min_overlap_ratio=0.05  # 降低重叠比例阈值，提高检测灵敏度
        )

        # 如果没有找到明确的区域，尝试使用连通区域分析
        if pointed_region is None:
            connected_boxes = self.image_processor.extract_connected_regions(
                book_binary,
                book_finger_mask,
                min_area=50,
                growth_iterations=3
            )

            # 合并重叠的框
            connected_boxes = self.image_processor.merge_overlapping_boxes(
                connected_boxes)

            if connected_boxes:
                # 选择最大的连通区域
                largest_box = max(
                    connected_boxes, key=lambda box: box[2] * box[3])

                # 扩展框
                largest_box = self.image_processor.expand_box(
                    largest_box,
                    (book_binary.shape[0], book_binary.shape[1]),
                    margin=10
                )

                content_box_relative = largest_box

                # 计算绝对坐标
                content_box_absolute = (
                    x + largest_box[0],
                    y + largest_box[1],
                    largest_box[2],
                    largest_box[3]
                )

                # 提取内容图像
                x_rel, y_rel, w_rel, h_rel = largest_box
                if (y_rel + h_rel <= book_image.shape[0] and
                    x_rel + w_rel <= book_image.shape[1] and
                    y_rel >= 0 and x_rel >= 0 and
                        w_rel > 0 and h_rel > 0):
                    content_image = book_image[
                        y_rel:y_rel+h_rel,
                        x_rel:x_rel+w_rel
                    ].copy()
                else:
                    # 处理越界情况
                    y_rel = max(0, min(y_rel, book_image.shape[0]-1))
                    x_rel = max(0, min(x_rel, book_image.shape[1]-1))
                    h_rel = min(h_rel, book_image.shape[0]-y_rel)
                    w_rel = min(w_rel, book_image.shape[1]-x_rel)

                    if h_rel > 0 and w_rel > 0:
                        content_image = book_image[
                            y_rel:y_rel+h_rel,
                            x_rel:x_rel+w_rel
                        ].copy()
                    else:
                        content_image = np.zeros((10, 10, 3), dtype=np.uint8)
            else:
                return PointedContentResult(
                    book_detection=best_book,
                    book_box=book_box,
                    content_box_relative=None,
                    content_box_absolute=None,
                    book_image=book_image,
                    book_binary=book_binary,
                    content_image=None,
                    finger_detection=best_finger,
                    processing_time=(time.time() - start_time) * 1000,
                    success=False,
                    error_message="未找到指向的内容"
                )
        else:
            # 使用找到的指向区域
            content_box_relative = pointed_region.box

            # 计算绝对坐标
            content_box_absolute = (
                x + pointed_region.box[0],
                y + pointed_region.box[1],
                pointed_region.box[2],
                pointed_region.box[3]
            )

            # 提取内容图像
            x_rel, y_rel, w_rel, h_rel = pointed_region.box
            content_image = book_image[
                y_rel:y_rel+h_rel,
                x_rel:x_rel+w_rel
            ].copy()

        # 生成最终结果
        result = PointedContentResult(
            book_detection=best_book,
            book_box=book_box,
            content_box_relative=content_box_relative,
            content_box_absolute=content_box_absolute,
            book_image=book_image,
            book_binary=book_binary,
            content_image=content_image,
            finger_detection=best_finger,
            processing_time=(time.time() - start_time) * 1000,
            success=True,
            error_message=None
        )

        return result

    def visualize_result(self, image: np.ndarray, result: PointedContentResult) -> np.ndarray:
        """
        在图像上可视化检测结果

        参数:
            image: 输入图像
            result: 检测结果

        返回:
            np.ndarray: 可视化后的图像
        """
        if not result.success or image is None:
            return image.copy() if image is not None else np.zeros((100, 100, 3), dtype=np.uint8)

        vis_image = image.copy()

        # 绘制书本边界框
        if result.book_box:
            x, y, w, h = result.book_box
            cv2.rectangle(vis_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
            cv2.putText(
                vis_image,
                f"Book: {result.book_detection.confidence:.2f}" if result.book_detection else "Book",
                (x, y-10),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.5,
                (0, 255, 0),
                2
            )

        # 绘制内容边界框
        if result.content_box_absolute:
            x, y, w, h = result.content_box_absolute
            cv2.rectangle(vis_image, (x, y), (x+w, y+h), (0, 0, 255), 2)
            cv2.putText(
                vis_image,
                "Pointed Content",
                (x, y-10),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.5,
                (0, 0, 255),
                2
            )

        # 绘制手指检测结果
        if result.finger_detection:
            # 绘制指尖
            cv2.circle(
                vis_image,
                result.finger_detection.tip_position,
                5,
                (255, 0, 0),
                -1
            )

            # 绘制指向方向线
            tip_x, tip_y = result.finger_detection.tip_position
            direction_x, direction_y = result.finger_detection.direction
            end_x = int(tip_x + direction_x * 100)
            end_y = int(tip_y + direction_y * 100)

            cv2.line(
                vis_image,
                (tip_x, tip_y),
                (end_x, end_y),
                (255, 0, 0),
                2
            )

            # 标注手指类型
            finger_type_names = ['拇指', '食指', '中指', '无名指', '小指']
            hand_type = "左" if hasattr(
                result.finger_detection, 'is_left_hand') and result.finger_detection.is_left_hand else "右"
            finger_name = f"{hand_type}{finger_type_names[result.finger_detection.finger_type]}"

            cv2.putText(
                vis_image,
                finger_name,
                (tip_x + 10, tip_y - 10),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.5,
                (255, 0, 0),
                2
            )

        # 添加处理时间信息
        cv2.putText(
            vis_image,
            f"Time: {result.processing_time:.1f} ms",
            (10, 30),
            cv2.FONT_HERSHEY_SIMPLEX,
            1,
            (0, 255, 255),
            2
        )

        return vis_image

    def process_frame(self, image: Optional[np.ndarray] = None, visualize: bool = True) -> Tuple[PointedContentResult, Optional[np.ndarray]]:
        """
        处理单帧图像，执行完整的检测流程

        参数:
            image: 输入图像，如果为None则从摄像头获取
            visualize: 是否生成可视化结果

        返回:
            Tuple[PointedContentResult, Optional[np.ndarray]]: (检测结果, 可视化图像)
        """
        # 获取图像
        if image is None:
            image = self.camera.capture_frame()
            if image is None:
                result = PointedContentResult(
                    book_detection=None,
                    book_box=None,
                    content_box_relative=None,
                    content_box_absolute=None,
                    book_image=None,
                    book_binary=None,
                    content_image=None,
                    finger_detection=None,
                    processing_time=0,
                    success=False,
                    error_message="无法获取摄像头图像"
                )
                return result, None

        # 执行检测
        result = self.detect_content(image)

        # 可视化结果
        vis_image = None
        if visualize:
            vis_image = self.visualize_result(image, result)

        return result, vis_image

    def run_continuous(self, callback=None, visualize: bool = True, fps_limit: int = 15):
        """
        连续运行检测过程

        参数:
            callback: 回调函数，接收检测结果和可视化图像
            visualize: 是否生成可视化结果
            fps_limit: 帧率限制
        """
        # 确保摄像头已打开
        if not self.camera.is_opened() and not self.camera.open():
            print("无法打开摄像头")
            return

        frame_time = 1.0 / fps_limit if fps_limit > 0 else 0
        last_frame_time = 0

        try:
            while True:
                current_time = time.time()

                # 限制帧率
                if current_time - last_frame_time < frame_time:
                    time.sleep(0.001)  # 小睡一下，减少CPU使用
                    continue

                last_frame_time = current_time

                # 处理帧
                result, vis_image = self.process_frame(visualize=visualize)

                # 执行回调
                if callback:
                    stop = callback(result, vis_image)
                    if stop:
                        break

                # 调试模式下显示图像
                if self.debug_mode and vis_image is not None:
                    cv2.imshow("ContentDetector", vis_image)
                    key = cv2.waitKey(1) & 0xFF
                    if key == 27 or key == ord('q'):  # ESC 或 q 键退出
                        break

        finally:
            # 释放资源
            if self.debug_mode:
                cv2.destroyAllWindows()

    def close(self):
        """关闭并释放资源"""
        self.camera.close()
